﻿using DotNetCommon;
using DotNetCommon.Accessors;
using DotNetCommon.Data;
using DotNetCommon.Extensions;
using DotNetCommon.Logger;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

namespace DBUtil.FastEntity.Builders
{
    /// <summary>
    /// 更新构造器
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class UpdateBuilder<T> : Builder<T> where T : class, new()
    {
        protected static readonly ILogger<InsertBuilder<T>> logger = LoggerFactory.CreateLogger<InsertBuilder<T>>();

        public EnumUpdateBuilderType Type;
        public bool _isEnableUpdateKeyValue { get; set; }
        public bool IsEnableUpdateKeyValue => _isEnableUpdateKeyValue;

        #region 初始化
        public T[] Entities { get; set; }
        public UpdateBuilder(EntityManager<T> entityManager, T[] entities) : base(entityManager)
        {
            Entities = entities;
            Type = EnumUpdateBuilderType.EntityArray;
        }
        public UpdateBuilder(EntityManager<T> entityManager, IEnumerable<T> entities) : base(entityManager)
        {
            Entities = entities.ToArray();
            Type = EnumUpdateBuilderType.EntityArray;
        }

        public Expression<Func<T, T>> Expression { get; set; }
        public UpdateBuilder(EntityManager<T> entityManager, Expression<Func<T, T>> expression) : base(entityManager)
        {
            Expression = expression;
            Type = EnumUpdateBuilderType.Expression;
        }

        public object Dto { get; }
        public UpdateBuilder(EntityManager<T> entityManager, object dto) : base(entityManager)
        {
            Dto = dto;
            Type = EnumUpdateBuilderType.Dto;
        }
        #endregion
        #region 超时设置
        private int? _timeoutSeconds { get; set; }
        public int? TimeoutSeconds => _timeoutSeconds;
        public virtual UpdateBuilder<T> CommandTimeout(int timeoutSeconds)
        {
            _timeoutSeconds = timeoutSeconds;
            return this;
        }
        #endregion
        #region ConfigEntitys
        public List<Action<EntityInfo>> ConfigSetEntitys { get; set; } = new List<Action<EntityInfo>>();
        public UpdateBuilder<T> ConfigSetEntity(Action<EntityInfo> action)
        {
            ConfigSetEntitys.Add(action);
            return this;
        }
        public List<Action<EntityInfo>> ConfigWhereEntitys { get; set; } = new List<Action<EntityInfo>>();
        public UpdateBuilder<T> ConfigWhereEntity(Action<EntityInfo> action)
        {
            ConfigWhereEntitys.Add(action);
            return this;
        }
        #endregion
        #region GetComputeEntity
        protected EntityInfo GetComputeSetEntity()
        {
            var res = entityManager.EntityInfo.Clone();
            foreach (var act in ConfigSetEntitys)
            {
                act?.Invoke(res);
            }
            res.EntityPropertyInfos = res.EntityPropertyInfos.Where(i => i.IsColumn && !i.IsIgnoreUpdate).ToList();
            return res;
        }

        protected EntityInfo GetComputeWhereEntity()
        {
            var res = entityManager.EntityInfo.Clone();
            foreach (var act in ConfigWhereEntitys)
            {
                act?.Invoke(res);
            }
            return res;
        }
        #endregion
        #region EnableUpdateKeyValue
        public UpdateBuilder<T> EnableUpdateKeyValue()
        {
            _isEnableUpdateKeyValue = true;
            return this;
        }
        public UpdateBuilder<T> DisableUpdateKeyValue()
        {
            _isEnableUpdateKeyValue = false;
            return this;
        }
        public UpdateBuilder<T> SwitchUpdateKeyValue(bool isEnableUpdateKeyValue)
        {
            _isEnableUpdateKeyValue = isEnableUpdateKeyValue;
            return this;
        }
        #endregion
        #region SetRaw
        //忽略列的大小写
        private Dictionary<string, object> _rawDic;
        public UpdateBuilder<T> SetRaw(string colName, object val)
        {
            _rawDic ??= new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
            if (_rawDic.ContainsKey(colName)) _rawDic.Remove(colName);
            _rawDic.Add(colName, val);
            return this;
        }
        #endregion
        #region Where
        protected List<Expression<Func<T, bool>>> Filters { get; set; }
        protected List<string> RawFilters { get; set; }
        public UpdateBuilder<T> Where(Expression<Func<T, bool>> filter)
        {
            Filters ??= new List<Expression<Func<T, bool>>>();
            Filters.Add(filter);
            return this;
        }
        public UpdateBuilder<T> WhereRaw(string filter)
        {
            RawFilters ??= new List<string>();
            RawFilters.Add(filter);
            return this;
        }
        #endregion
        #region 执行
        public virtual int ExecuteAffrows() => db.ExecuteSql(ToSql());
        #endregion

        private static ConcurrentDictionary<Type, ObjectAccessor> accessorCache = new ConcurrentDictionary<Type, ObjectAccessor>();
        private static ConcurrentDictionary<Type, string[]> typeProps = new ConcurrentDictionary<Type, string[]>();
        public string ToSql()
        {
            var (Models, entityInfo) = ComputeModels();
            var primary = entityInfo.PrimaryKeyColumn;

            //<rowkey,<col,colval>>
            var setDic = new Dictionary<string, Dictionary<string, string>>();
            var sql = string.Empty;
            //audit
            var now = string.Empty;
            string userId = null;
            if (entityInfo.IsAudit)
            {
                now = entityManager.GetNow();
                userId = entityManager.GetUserId();
            }
            var hasWhere = false;
            if (Type == EnumUpdateBuilderType.EntityArray)
            {
                //当且仅当 更新的实体只有一个 且有其他 Where 条件时才可以设置 IsEnableUpdateKeyValue=true
                if (Entities.Length == 0) throw new Exception($"必须指定更新的数据!");
                if (Entities.Length == 1)
                {
                    if (IsEnableUpdateKeyValue && Filters.IsNullOrEmpty() && RawFilters.IsNullOrEmpty()) throw new Exception($"当仅更新一个实体,且没有指定Where条件时，EnableUpdateKey 无效!");
                }
                if (Entities.Length > 1 && IsEnableUpdateKeyValue) throw new Exception($"当更新多个实体时,EnableUpdateKey 无效!");

                #region 更新实体或实体集合
                Dictionary<string, string> setRow = null;
                string primaryValSeg = string.Empty;
                var len = Entities.Length;
                var cols = new List<string>();
                for (int i = 0; i < len; i++)
                {
                    var Model = Entities[i];
                    //PK
                    var primaryVal = primary.PropertyInfo.GetValue(Model);
                    primaryValSeg = db.ConvertToSqlSegment(primaryVal).UnWrap();
                    setRow = new Dictionary<string, string>();
                    setDic.Add(primaryValSeg, setRow);
                    if (IsEnableUpdateKeyValue)
                    {
                        if (i == 0) cols.Add(primary.ColumnName);
                        setRow.Add(primary.ColumnName, primaryValSeg);
                    }

                    object val = null;
                    //Normal 列
                    foreach (var col in entityInfo.NormalColumns)
                    {
                        if (_rawDic.IsNotNullOrEmpty() && _rawDic.ContainsKey(col.ColumnName)) continue;
                        if (i == 0) cols.Add(col.ColumnName);
                        val = col.PropertyInfo.GetValue(Model);
                        #region RawString
                        if (col.IsRawString)
                        {
                            setRow.Add(col.ColumnName, val.ToString());
                            continue;
                        }
                        #endregion
                        #region json
                        if (col.IsStoreJsonFast)
                        {
                            var json = val?.ToJsonFast(dateFormatString: col.StoreJsonFastDateFormatString
                                , isLongToString: col.StoreJsonFastIsLongToString
                                , IgnoreNull: col.StoreJsonFastIgnoreNull
                                , enum2String: col.StoreJsonFastEnum2String
                                , lowerCamelCase: col.StoreJsonFastLowerCamelCase
                                , isIntend: col.StoreJsonFastIsIntend
                                , isAllToString: col.StoreJsonFastIsAllToString
                                , allNumDigit: col.StoreJsonFastAllNumDigit
                                , decimalDigit: col.StoreJsonFastDecimalDigit);
                            setRow.Add(col.ColumnName, db.ConvertToSqlSegment(json).UnWrap());
                            continue;
                        }
                        if (col.IsStoreJson)
                        {
                            var json = val?.ToJson();
                            setRow.Add(col.ColumnName, db.ConvertToSqlSegment(json).UnWrap());
                            continue;
                        }
                        #endregion
                        setRow.Add(col.ColumnName, db.ConvertToSqlSegment(val).UnWrap());
                    }
                    //Audit 列
                    foreach (var col in entityInfo.AuditColumns)
                    {
                        if (col.IsAuditUpdateTime)
                        {
                            if (i == 0) cols.Add(col.ColumnName);
                            setRow.Add(col.ColumnName, now);
                            continue;
                        }
                        if (col.IsAuditUpdateUserId)
                        {
                            if (i == 0) cols.Add(col.ColumnName);
                            setRow.Add(col.ColumnName, userId == "null" ? db.ConvertToSqlSegment(col.AuditDefaultValue).Data : userId);
                            continue;
                        }
                        //create delete
                        continue;
                    }

                    //SetRraw 列
                    if (_rawDic.IsNotNullOrEmpty())
                    {
                        foreach (var item in _rawDic)
                        {
                            if (string.Compare(item.Key, primary.ColumnName, true) != 0)
                                setRow.Add(item.Key, db.ConvertToSqlSegment(item.Value).UnWrap());
                        }
                    }
                }
                if (setDic.Count == 1)
                {
                    //单个实体更新
                    var setSql = setRow.Select(i => $"{i.Key} = {i.Value}").ToStringSeparated(",\r\n    ");
                    if (!IsEnableUpdateKeyValue)
                    {
                        var whereSql = $"where {primary.ColumnName}={primaryValSeg};";
                        sql = $"update {entityInfo.FullName} set \r\n    {setSql}\r\n{whereSql}";
                        hasWhere = true;
                    }
                }
                else
                {
                    //多个实体更新
                    var len2 = cols.Count;
                    var sb = new StringBuilder();
                    var primaryValues = new List<string>();
                    for (int i = 0; i < len2; i++)
                    {
                        var colName = cols[i];
                        if (i > 0)
                        {
                            sb.Append("\r\n").Append("    ");
                        }
                        sb.Append(colName).Append(" = case ").Append(primary.ColumnName);
                        foreach (var row in setDic)
                        {
                            var primaryKeyValue = row.Key;
                            if (i == 0) primaryValues.Add(primaryKeyValue);
                            sb.Append("\r\n").Append("        when ").Append(primaryKeyValue).Append(" then ").Append(row.Value[colName]);
                        }
                        sb.Append(" end");
                        if (i < len2 - 1) sb.Append(",");
                    }
                    var setSql = sb.ToString();
                    var whereSql = $"where {primary.ColumnName} in ({primaryValues.ToStringSeparated(",")});";
                    sql = $"update {entityInfo.FullName} set \r\n    {setSql}\r\n{whereSql}";
                    hasWhere = true;
                }
                #endregion
            }
            else
            {
                #region dto或者是Expression
                var isDto = false;
                //从expression简化得来的dto,存放原expression中的属性声明
                List<string> limitDtoProps = null;
                object tmpDto = null;
                Expression lambda = null;
                if (Type == EnumUpdateBuilderType.Dto)
                {
                    isDto = true;
                    tmpDto = Dto;
                }
                else if (Type == EnumUpdateBuilderType.Expression)
                {
                    lambda = ExpressionHelper.ReduceLambda(Expression);
                    if (lambda.NodeType == ExpressionType.Constant)
                    {
                        isDto = true;
                        tmpDto = (lambda as ConstantExpression).Value;
                        limitDtoProps = ExpressionHelper.GetInitOrReturnPropNames(Expression);
                    }
                }
                if (isDto)
                {
                    #region dto
                    var dtoType = tmpDto.GetType();
                    var accessor = accessorCache.GetOrAdd(dtoType, type => Accessor.Build(type));
                    object primaryVal = null;
                    if (accessor.Properties.ContainsKey(primary.PropName))
                    {
                        primaryVal = accessor[tmpDto, primary.PropName];
                    }

                    if ((!entityInfo.IsValidPrimaryValue(primaryVal)) && Filters.IsNullOrEmpty() && RawFilters.IsNullOrEmpty()) throw new Exception("使用dto更新时必须指定主键或过滤条件!");

                    object val = null;
                    var setRow = new Dictionary<string, string>();
                    var whereSql = "";

                    //PK
                    if (IsEnableUpdateKeyValue)
                    {
                        if (!entityInfo.IsValidPrimaryValue(primaryVal)) throw new Exception($"使用dto更新主键时,主键不能为空!");
                        setRow.Add(primary.ColumnName, db.ConvertToSqlSegment(primaryVal).UnWrap());
                    }
                    else
                    {
                        //dto更新的时候 只有指定有效主键的才会应用到 where
                        if (entityInfo.IsValidPrimaryValue(primaryVal))
                        {
                            var primaryValSeg = db.ConvertToSqlSegment(primaryVal).UnWrap();
                            whereSql = $"where {primary.ColumnName}={primaryValSeg}";
                        }
                    }

                    foreach (var col in entityInfo.NormalColumns)
                    {
                        if (_rawDic.IsNotNullOrEmpty() && _rawDic.ContainsKey(col.ColumnName)) continue;
                        val = accessor[tmpDto, col.PropName];
                        #region RawString
                        if (col.IsRawString)
                        {
                            setRow.Add(col.ColumnName, val.ToString());
                            continue;
                        }
                        #endregion
                        #region json
                        if (col.IsStoreJsonFast)
                        {
                            var json = val?.ToJsonFast(dateFormatString: col.StoreJsonFastDateFormatString
                                , isLongToString: col.StoreJsonFastIsLongToString
                                , IgnoreNull: col.StoreJsonFastIgnoreNull
                                , enum2String: col.StoreJsonFastEnum2String
                                , lowerCamelCase: col.StoreJsonFastLowerCamelCase
                                , isIntend: col.StoreJsonFastIsIntend
                                , isAllToString: col.StoreJsonFastIsAllToString
                                , allNumDigit: col.StoreJsonFastAllNumDigit
                                , decimalDigit: col.StoreJsonFastDecimalDigit);
                            setRow.Add(col.ColumnName, db.ConvertToSqlSegment(json).UnWrap());
                            continue;
                        }
                        if (col.IsStoreJson)
                        {
                            var json = val?.ToJson();
                            setRow.Add(col.ColumnName, db.ConvertToSqlSegment(json).UnWrap());
                            continue;
                        }
                        #endregion
                        setRow.Add(col.ColumnName, db.ConvertToSqlSegment(val).UnWrap());
                    }

                    //Audit 列
                    foreach (var col in entityInfo.AuditColumns)
                    {
                        if (col.IsAuditUpdateTime)
                        {
                            setRow.Add(col.ColumnName, now);
                            continue;
                        }
                        if (col.IsAuditUpdateUserId)
                        {
                            setRow.Add(col.ColumnName, userId == "null" ? db.ConvertToSqlSegment(col.AuditDefaultValue).Data : userId);
                            continue;
                        }
                        //create delete
                        continue;
                    }
                    //SetRaw 列
                    if (_rawDic.IsNotNullOrEmpty())
                    {
                        foreach (var item in _rawDic)
                        {
                            if (string.Compare(item.Key, primary.ColumnName, true) != 0)
                                setRow.Add(item.Key, db.ConvertToSqlSegment(item.Value).UnWrap());
                        }
                    }

                    var setSql = setRow.Select(i => $"{i.Key} = {i.Value}").ToStringSeparated(",\r\n    ");
                    if (whereSql.IsNotNullOrEmptyOrWhiteSpace())
                    {
                        sql = $"update {entityInfo.FullName} set \r\n    {setSql}\r\n{whereSql}";
                        hasWhere = true;
                    }
                    else
                    {
                        sql = $"update {entityInfo.FullName} set \r\n    {setSql}\r\n";
                    }
                    #endregion
                }
                else
                {
                    #region Expression
                    sql = $"update {entityInfo.TableName} set ";
                    var whereSql = "";
                    var body = (lambda as LambdaExpression).Body;
                    var initExp = body as MemberInitExpression;
                    var bindings = initExp.Bindings;
                    var setRow = new Dictionary<string, string>();

                    //Normal 列
                    foreach (var col in entityInfo.NormalColumns)
                    {
                        if (_rawDic.IsNotNullOrEmpty() && _rawDic.ContainsKey(col.ColumnName)) continue;

                        #region 尝试求值(主键列,且Expression中没有赋值的不能求出)
                        var binding = bindings.FirstOrDefault(i => i.Member.Name == col.PropName);
                        string val = "null";
                        if (binding != null)
                        {
                            var assign = binding as MemberAssignment;
                            var exp = assign.Expression;
                            if (exp.NodeType == ExpressionType.Constant)
                            {
                                val = db.ConvertToSqlSegment((exp as ConstantExpression).Value).Data;
                            }
                            else
                            {
                                val = Visit(exp);
                            }
                        }
                        #endregion
                        #region 主键
                        if (col.IsPrimaryKey)
                        {
                            if (IsEnableUpdateKeyValue)
                            {
                                if (val == "null" || val == db.ConvertToSqlSegment(primary.DefaultValue).Data) throw new Exception($"使用Expression更新主键时,主键不能为空!");
                                setRow.Add(col.ColumnName, val);
                            }
                            else
                            {
                                if (val != "null")
                                {
                                    whereSql += $"{primary.ColumnName} = {val}";
                                    hasWhere = true;
                                }
                            }
                            continue;
                        }
                        #endregion
                        #region RawString
                        if (col.IsRawString)
                        {
                            setRow.Add(col.ColumnName, val.ToString());
                            continue;
                        }
                        #endregion
                        #region json
                        if (col.IsStoreJsonFast)
                        {
                            var json = val?.ToJsonFast(dateFormatString: col.StoreJsonFastDateFormatString
                                , isLongToString: col.StoreJsonFastIsLongToString
                                , IgnoreNull: col.StoreJsonFastIgnoreNull
                                , enum2String: col.StoreJsonFastEnum2String
                                , lowerCamelCase: col.StoreJsonFastLowerCamelCase
                                , isIntend: col.StoreJsonFastIsIntend
                                , isAllToString: col.StoreJsonFastIsAllToString
                                , allNumDigit: col.StoreJsonFastAllNumDigit
                                , decimalDigit: col.StoreJsonFastDecimalDigit);
                            setRow.Add(col.ColumnName, db.ConvertToSqlSegment(json).UnWrap());
                            continue;
                        }
                        if (col.IsStoreJson)
                        {
                            var json = val?.ToJson();
                            setRow.Add(col.ColumnName, db.ConvertToSqlSegment(json).UnWrap());
                            continue;
                        }
                        #endregion
                        setRow.Add(col.ColumnName, val);
                    }

                    //Audit
                    foreach (var col in entityInfo.NormalColumns)
                    {
                        if (col.IsAuditUpdateTime)
                        {
                            setRow.Add(col.ColumnName, now);
                            continue;
                        }
                        if (col.IsAuditUpdateUserId)
                        {
                            setRow.Add(col.ColumnName, userId == "null" ? db.ConvertToSqlSegment(col.AuditDefaultValue).Data : userId);
                            continue;
                        }
                        //create delete
                        continue;
                    }
                    //SetRaw
                    if (_rawDic.IsNotNullOrEmpty())
                    {
                        foreach (var item in _rawDic)
                        {
                            if (string.Compare(item.Key, primary.ColumnName, true) != 0)
                                setRow.Add(item.Key, db.ConvertToSqlSegment(item.Value).UnWrap());
                        }
                    }
                    var setSql = setRow.Select(i => $"{i.Key} = {i.Value}").ToStringSeparated(",\r\n    ");
                    if (whereSql.IsNotNullOrEmptyOrWhiteSpace())
                    {
                        sql = $"update {entityInfo.FullName} set \r\n    {setSql}\r\n{whereSql}";
                        hasWhere = true;
                    }
                    else
                    {
                        sql = $"update {entityInfo.FullName} set \r\n    {setSql}\r\n";
                    }
                    #endregion
                }
                #endregion
            }

            //处理过滤条件
            var whereEntityInfo = GetComputeWhereEntity();
            var filterSqls = new List<string>();
            if (Filters.IsNotNullOrEmpty())
            {
                foreach (var Filter in Filters)
                {
                    var exp = ExpressionHelper.ReduceLambda(Filter);
                    if (exp.NodeType == ExpressionType.Constant)
                    {
                        var b = (exp as ConstantExpression).Value.To<bool>();
                        if (b) filterSqls.Add("1=1");
                        else filterSqls.Add("1=0");
                    }
                    else
                    {
                        var tmp = GenerateUpdateWhere(exp, whereEntityInfo);
                        if (tmp.IsNotNullOrEmptyOrWhiteSpace()) filterSqls.Add(tmp);
                    }
                }
            }
            if (RawFilters.IsNotNullOrEmpty())
            {
                filterSqls.AddRange(RawFilters);
            }
            if (filterSqls.Count == 1)
            {
                if (hasWhere) sql += $" and({filterSqls.FirstOrDefault()})";
                else sql += $" where {filterSqls.FirstOrDefault()}";
            }
            else
            {
                if (filterSqls.Count > 0)
                {
                    var tmp = filterSqls.Select(i => $"({i})").ToStringSeparated(" and ");
                    if (hasWhere) sql += $" and ({tmp})";
                    else sql += $" where {tmp}";
                }
            }
            sql += ";";
            return sql;

            //求出表达式的具体值
            string Visit(Expression exp)
            {
                switch (exp.NodeType)
                {
                    case ExpressionType.Constant:
                        var val = (exp as ConstantExpression).Value;
                        return db.ConvertToSqlSegment(val).Data;
                    case ExpressionType.MemberAccess:
                        var member = exp as MemberExpression;
                        var name = member.Member.Name;
                        if (member.Member.DeclaringType == entityInfo.Type)
                        {
                            name = entityInfo.EntityPropertyInfos.FirstOrDefault(i => i.PropName == name).ColumnName;
                        }
                        return name;
                    case ExpressionType.Add:
                    case ExpressionType.Subtract:
                    case ExpressionType.Multiply:
                    case ExpressionType.Divide:
                    case ExpressionType.Equal:
                    case ExpressionType.NotEqual:
                    case ExpressionType.GreaterThan:
                    case ExpressionType.GreaterThanOrEqual:
                    case ExpressionType.LessThan:
                    case ExpressionType.LessThanOrEqual:
                        {
                            var binary = exp as BinaryExpression;
                            var left = Visit(binary.Left);
                            var right = Visit(binary.Right);
                            if (binary.Left.Type == typeof(string))
                            {
                                //字符串相加
                                return db.StringSqlSegment.Add(left, right);
                            }
                            return $"{left} {_cache[exp.NodeType]} {right}";
                        }
                    case ExpressionType.Convert:
                        {
                            var unary = exp as UnaryExpression;
                            var destType = unary.Type;
                            var fromType = unary.Operand.Type;

                            if (destType.IsNullable() && destType.GenericTypeArguments[0] == fromType)
                            {
                                // 比如: new Person(){ DateTime? = DateTime}
                                // 直接忽略
                                return Visit(unary.Operand);
                            }

                            break;
                        }
                    default:
                        break;
                }
                throw new Exception($"不能处理的表达式类型: {exp.NodeType}, {exp}");
            }
        }
        public string GenerateUpdateWhere(Expression expression, EntityInfo whereEntityInfo)
        {
            var body = (expression as LambdaExpression).Body;
            return Visit(body);

            string Visit(Expression exp)
            {
                switch (exp.NodeType)
                {
                    case ExpressionType.Constant:
                        var val = (exp as ConstantExpression).Value;
                        return db.ConvertToSqlSegment(val).Data;
                    case ExpressionType.MemberAccess:
                        {
                            var member = exp as MemberExpression;
                            var name = member.Member.Name;
                            if (member.Member.DeclaringType == whereEntityInfo.Type)
                            {
                                //直接就是 p.Name
                                name = whereEntityInfo.EntityPropertyInfos.FirstOrDefault(i => i.PropName == name).ColumnName;
                            }
                            else if (member.Member.Name == "Length" && member.Member.DeclaringType == typeof(string))
                            {
                                // P.Name.Length
                                var tmp = Visit(member.Expression);
                                return db.StringSqlSegment.GetLength(tmp.WrapBracket(), true);
                            }
                            return name;
                        }
                    case ExpressionType.Add:
                        {
                            var binary = exp as BinaryExpression;
                            var left = Visit(binary.Left);
                            var right = Visit(binary.Right);
                            if (binary.Left.Type == typeof(string))
                            {
                                return db.StringSqlSegment.Add(left, right);
                            }
                            return $"{left.WrapBracket()} {_cache[exp.NodeType]} {right.WrapBracket()}";
                        }
                    case ExpressionType.Subtract:
                    case ExpressionType.Multiply:
                    case ExpressionType.Divide:
                        {
                            var binary = exp as BinaryExpression;
                            var left = Visit(binary.Left);
                            var right = Visit(binary.Right);
                            return $"{left.WrapBracket()} {_cache[exp.NodeType]} {right.WrapBracket()}";
                        }
                    case ExpressionType.Equal:
                    case ExpressionType.NotEqual:
                        {
                            var binary = exp as BinaryExpression;
                            var left = Visit(binary.Left);
                            var right = Visit(binary.Right);
                            if (left == "null")
                            {
                                return $"{right.WrapBracket()} {(exp.NodeType == ExpressionType.Equal ? "is" : "is not")} null";
                            }
                            else if (right == "null")
                            {
                                return $"{left.WrapBracket()} {(exp.NodeType == ExpressionType.Equal ? "is" : "is not")} null";
                            }
                            return $"{left.WrapBracket()} {_cache[exp.NodeType]} {right.WrapBracket()}";
                        }
                    case ExpressionType.GreaterThan:
                    case ExpressionType.GreaterThanOrEqual:
                    case ExpressionType.LessThan:
                    case ExpressionType.LessThanOrEqual:
                    case ExpressionType.AndAlso:
                    case ExpressionType.OrElse:
                        {
                            var binary = exp as BinaryExpression;
                            var left = Visit(binary.Left);
                            var right = Visit(binary.Right);
                            return $"{left.WrapBracket()} {_cache[exp.NodeType]} {right.WrapBracket()}";
                        }
                    case ExpressionType.Convert:
                        {
                            var unary = exp as UnaryExpression;
                            var destType = unary.Type;
                            var fromType = unary.Operand.Type;

                            if ((destType.IsNullable() && destType.GenericTypeArguments[0] == fromType) || (fromType.IsNullable() && fromType.GenericTypeArguments[0] == destType))
                            {
                                // DateTime? => DateTime 或 DateTime=>DateTime?
                                // 直接忽略
                                return Visit(unary.Operand);
                            }
                            if (fromType.IsNullable()) fromType = fromType.GenericTypeArguments[0];
                            if (fromType.IsEnum)
                            {
                                // 忽略枚举转数字
                                return Visit(unary.Operand);
                            }
                            break;
                        }
                    case ExpressionType.Call:
                        {
                            var call = exp as MethodCallExpression;
                            var method = call.Method.GetMethodGenericFullName();
                            switch (method.Name)
                            {
                                case "bool string.StartsWith(string value)":
                                    {
                                        var op1 = Visit(call.Object);
                                        var arg = Visit(call.Arguments[0]);
                                        arg = arg.Substring(0, arg.Length - 1) + "%'";
                                        var sql = $"{op1.WrapBracket()} like {arg}";
                                        return sql;
                                    }
                                case "bool string.EndsWith(string value)":
                                    {
                                        var op1 = Visit(call.Object);
                                        var arg = Visit(call.Arguments[0]);
                                        arg = "'%" + arg.Substring(1, arg.Length - 1);
                                        var sql = $"{op1.WrapBracket()} like {arg}";
                                        return sql;
                                    }
                                case "bool string.Contains(string value)":
                                    {
                                        var op1 = Visit(call.Object);
                                        var arg = Visit(call.Arguments[0]);
                                        arg = "'%" + arg.Substring(1, arg.Length - 2) + "%'";
                                        var sql = $"{op1.WrapBracket()} like {arg}";
                                        return sql;
                                    }
                                case "bool System.Collections.Generic.List<T>.Contains(T item)":
                                    {
                                        var op1 = Visit(call.Object);
                                        var arg = Visit(call.Arguments[0]);
                                        var sql = $"{arg.WrapBracket()} in {op1}";
                                        return sql;
                                    }
                                case "bool System.Linq.Enumerable<T>.Contains(System.Collections.Generic.IEnumerable<T> source, byte value)":
                                    {
                                        var op1 = Visit(call.Arguments[0]);
                                        var arg = Visit(call.Arguments[1]);
                                        var sql = $"{arg.WrapBracket()} in {op1}";
                                        return sql;
                                    }
                                case "bool DotNetCommon.Extensions.StringExtensions.IsNullOrEmpty(string value)":
                                    {
                                        var op1 = Visit(call.Arguments[0]);
                                        return $"{op1.WrapBracket()} is null or {op1.WrapBracket()} = ''";
                                    }
                                case "bool DotNetCommon.Extensions.StringExtensions.IsNotNullOrEmpty(string value)":
                                    {
                                        var op1 = Visit(call.Arguments[0]);
                                        return $"{op1.WrapBracket()} is not null and {op1.WrapBracket()} <> ''";
                                    }
                                case "bool DotNetCommon.Extensions.StringExtensions.IsNullOrEmptyOrWhiteSpace(string value)":
                                    {
                                        var op1 = Visit(call.Arguments[0]);
                                        return $"{op1.WrapBracket()} is null or {db.StringSqlSegment.Trim(op1.WrapBracket())} = ''";
                                    }
                                case "bool DotNetCommon.Extensions.StringExtensions.IsNotNullOrEmptyOrWhiteSpace(string value)":
                                    {
                                        var op1 = Visit(call.Arguments[0]);
                                        return $"{op1.WrapBracket()} is not null and {db.StringSqlSegment.Trim(op1.WrapBracket())} <> ''";
                                    }
                                case "string string.ToUpper()":
                                    {
                                        var op1 = Visit(call.Object);
                                        return db.StringSqlSegment.Upper(op1.WrapBracket());
                                    }
                                case "string string.ToLower()":
                                    {
                                        var op1 = Visit(call.Object);
                                        return db.StringSqlSegment.Lower(op1.WrapBracket());
                                    }
                                case "string string.Trim()":
                                    {
                                        var op1 = Visit(call.Object);
                                        return db.StringSqlSegment.Trim(op1.WrapBracket());
                                    }
                                case "string string.TrimStart()":
                                    {
                                        var op1 = Visit(call.Object);
                                        return db.StringSqlSegment.LeftTrim(op1.WrapBracket());
                                    }
                                case "string string.TrimEnd()":
                                    {
                                        var op1 = Visit(call.Object);
                                        return db.StringSqlSegment.RightTrim(op1.WrapBracket());
                                    }
                                default:
                                    {
                                        throw new Exception($"不支持解析此lambda表达式:{method.Name}!");
                                    }
                            }
                        }
                    case ExpressionType.And:
                    case ExpressionType.Or:
                        {
                            var binary = exp as BinaryExpression;
                            var left = Visit(binary.Left);
                            var right = Visit(binary.Right);
                            if (binary.Left.Type == typeof(bool)) return $"{left.WrapBracket()} {_cache[exp.NodeType]} {right.WrapBracket()}";
                            else return $"{left.WrapBracket()} {(exp.NodeType == ExpressionType.And ? "&" : "|")} {right.WrapBracket()}";
                        }
                    default:
                        break;
                }
                throw new Exception($"不能处理的表达式类型: {exp.NodeType}, {exp}");
            }
        }
        private static Dictionary<ExpressionType, string> _cache = new Dictionary<ExpressionType, string>()
        {
            {ExpressionType.Add,"+" },
            {ExpressionType.Subtract,"-" },
            {ExpressionType.Multiply,"*" },
            {ExpressionType.Divide,"/" },

            {ExpressionType.Equal,"=" },
            {ExpressionType.NotEqual,"<>" },
            {ExpressionType.GreaterThan,">" },
            {ExpressionType.GreaterThanOrEqual,">=" },
            {ExpressionType.LessThan,"<" },
            {ExpressionType.LessThanOrEqual,"<=" },

            {ExpressionType.AndAlso,"and" },
            {ExpressionType.OrElse,"or" },
            {ExpressionType.And,"and" },
            {ExpressionType.Or,"or" },
        };

        public (T[] Models, EntityInfo entityInfo) ComputeModels()
        {
            if (Type == EnumUpdateBuilderType.EntityArray && Entities.Length == 0) throw new Exception("必须指定要更新的实体数据!");
            if (Type == EnumUpdateBuilderType.Expression && Expression == null) throw new Exception("必须指定要更新的表达式!");
            if (Type == EnumUpdateBuilderType.Dto && Dto == null) throw new Exception("必须指定要更新的dto!");

            var info = GetComputeSetEntity();

            T[] Models = null;
            if (Type == EnumUpdateBuilderType.EntityArray)
            {
                Models = Entities;
            }
            else if (Type == EnumUpdateBuilderType.Expression)
            {
                var names = ExpressionHelper.GetInitOrReturnPropNames(Expression);
                //移除非Expression包含的属性
                info.RemoveAllNormalColumnsExcept(names.ToArray());
            }
            else if (Type == EnumUpdateBuilderType.Dto)
            {
                var type = Dto.GetType();
                Type dtoType = type;
                //移除非Dto包含的属性(除了审计外)
                var names = dtoType.GetProperties().Select(i => i.Name);
                info.RemoveAllNormalColumnsExcept(names.ToArray());
            }

            return (Models, info);
        }
    }

    public enum EnumSqlGeneratorType
    {
        UpdateExpression,
        UpdateWhere,
        DeleteWhere,
        Select
    }

    public enum EnumUpdateBuilderType
    {
        EntityArray,
        Dto,
        Expression
    }

    internal static class StringExtensions
    {
        /// <summary>
        /// 处理括号是否加在两端
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        internal static string WrapBracket(this string str)
        {
            if (str == null) return str;
            //忽略两端空格的影响
            var tmp = str.Trim();
            //当表达式已有且仅有一对"()"在两端时,直接返回
            if (tmp.StartsWith("(") && tmp.EndsWith(")") && tmp.IndexOf(')') == tmp.Length - 1) return str;
            //当有空格时 直接加上"()"
            if (tmp.Contains(" ")) return $"({str})";
            return str;
        }
    }
}